/**
* project3D Engine
* Material that subdivides its polygons according to distance and size to get better texture quality
* @author John Sword
* @version 2 - AS3
*/

package engine.materials
{
	
	import engine.geom.Face;
	import engine.geom.UV;
	import engine.geom.Vertex;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	
	
	public class TextureLOD extends Material
	{


		public var smooth:Boolean = false;
	
		public var detail:int = 1;
		public var maxdetail:int = 3;
		public var fixedDetail:Boolean = false;
		public var useWire:Boolean = false;
		
		private static const abs:Function = Math.abs;
		
		public function TextureLOD ( tex:BitmapData, flip:Boolean = true )
		{
			super( tex );
			if(flip)
			{
				// flip texture
				// needs a temp mc to apply the invert matrix
				var texMc:Sprite = new Sprite();
				var bmp:Bitmap = new Bitmap ( tex );
				// apply texture to temp mc
				texMc.addChild( bmp );
				// apply invert
				var textureMatrix:Matrix = new Matrix();
				textureMatrix.a = 1;
				textureMatrix.b = 0;
				textureMatrix.c = 0;
				textureMatrix.d = -1;
				textureMatrix.tx = 0;
				textureMatrix.ty = tex.height;
				// recreate texture
				texture = new BitmapData(texMc.width, texMc.height, true, 0x00000000);
				texture.draw(texMc, textureMatrix);
			} else {
				texture = tex;
			}
		}
		
		public override function render ( face:Face, screen:Sprite ) : void
		{
			if ( !fixedDetail ) {
				//detail =  Math.round( 1000/face.Z );
				var x1:int = face.v1.screen.x;
				var y1:int = face.v1.screen.y;
				var x2:int = face.v2.screen.x;
				var y2:int = face.v2.screen.y;
				var x3:int = face.v3.screen.x;
				var y3:int = face.v3.screen.y;
				// area relative to distance
				detail = abs(((x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)) / face.Z);
				//detail = (detail ^ (detail >> 31)) - (detail >> 31);
			} else {
				detail = maxdetail;
			}
			//trace(detail)
			//wireAlpha = 1;
			//wireColor = 0xFF0000;
			if ( detail == 0 ) {
				renderFace( face, screen );
			} else {
				if ( detail > maxdetail ) detail = maxdetail;
				divide( face, screen, detail );
			}
		}
		
		private function divide ( face:Face, screen:Sprite, level:int ) : void
		{
			var faces:Array = subdivide ( face );
			if ( level > 0 ) level --;
			var i:int = faces.length;
			while ( --i > -1 )
			{
				if ( level > 0 )
				{
					divide ( faces[i], screen, level );
				} else {
					renderFace ( faces[i], screen );
				}
			}
		}
		
		private function subdivide ( face:Face ) : Array
		{
			
			/*
			* subdivide and construct new triangles
			*        b
            *       /\      
            *      /  \
            *   ab/____\bc	   
            *    /\    /\      
            *   /  \  /  \     
            *  /____\/____\    
            * a	    ca     c   
			*/
	
			var ax:int = face.v1.screen.x;
			var ay:int = face.v1.screen.y;
			var az:int = face.v1.screen.z;
			var bx:int = face.v2.screen.x;
			var by:int = face.v2.screen.y;
			var bz:int = face.v2.screen.z;
			var cx:int = face.v3.screen.x;
			var cy:int = face.v3.screen.y;
			var cz:int = face.v3.screen.z;
			
			var mabz:Number = 2 / (az + bz);
            var mbcz:Number = 2 / (bz + cz);
            var mcaz:Number = 2 / (cz + az);

            var mabx:Number = (ax*az + bx*bz) * mabz;
            var maby:Number = (ay*az + by*bz) * mabz;
            var mbcx:Number = (bx*bz + cx*cz) * mbcz;
            var mbcy:Number = (by*bz + cy*cz) * mbcz;
            var mcax:Number = (cx*cz + ax*az) * mcaz;
            var mcay:Number = (cy*cz + ay*az) * mcaz;
			
			var abuv:UV = createMidUV( face.uv1, face.uv2 );
            var bcuv:UV = createMidUV( face.uv2, face.uv3 );
            var cauv:UV = createMidUV( face.uv3, face.uv1 );
			
			//var m:Material = face.getMaterial();

			var abvx:Number = mabx*.5;
			var abvy:Number = maby*.5;
			var abvz:Number = (az+bz)*.5;
			
			var cavx:Number = mcax*.5;
			var cavy:Number = mcay*.5;
			var cavz:Number = (cz+az)*.5;
			
			var bcvx:Number = mbcx*.5;
			var bcvy:Number = mbcy*.5;
			var bcvz:Number = (bz+cz)*.5;
			
			var faces:Array = new Array();
			
			faces.push( buildTri(ax,ay,az,abvx,abvy,abvz,cavx,cavy,cavz,
						face.uv1,abuv,cauv, this) );
			
			faces.push( buildTri(abvx,abvy,abvz,bx,by,bz,bcvx,bcvy,bcvz,
							abuv,face.uv2,bcuv, this) );
							
			faces.push( buildTri(cavx,cavy,cavz,bcvx,bcvy,bcvz,cx,cy,cz,
							cauv,bcuv,face.uv3, this) );
							
			faces.push( buildTri(cavx,cavy,cavz,abvx,abvy,abvz,bcvx,bcvy,bcvz,
							cauv,abuv,bcuv, this) );
			
			return faces;
		}
		
		private function buildTri ( x1:Number,y1:Number,z1:Number,
										x2:Number,y2:Number,z2:Number,
											x3:Number,y3:Number,z3:Number,
												uv1:UV,uv2:UV,uv3:UV, m:Material ) : Face
		{
			var vtx1:Vertex = new Vertex();
			var vtx2:Vertex = new Vertex();
			var vtx3:Vertex = new Vertex();
			
			vtx1.screen.x = x1;
			vtx1.screen.y = y1;
			vtx1.screen.z = z1;
			vtx2.screen.x = x2;
			vtx2.screen.y = y2;
			vtx2.screen.z = z2;
			vtx3.screen.x = x3;
			vtx3.screen.y = y3;
			vtx3.screen.z = z3;
			
			return new Face ( vtx1, vtx2, vtx3, uv1, uv2, uv3, m );
		}
		
		private function renderFace ( face:Face, screen:Sprite ) : void
		{
			var g:Graphics = screen.graphics;
			
			// Draw triangle
			g.beginBitmapFill( texture, face.getTextureMatrix(), false, smooth );
			if ( useWire ) g.lineStyle( wireSize, wireColor, wireAlpha);
			g.moveTo( face.v1.screen.x, face.v1.screen.y );
			g.lineTo( face.v2.screen.x, face.v2.screen.y );
			g.lineTo( face.v3.screen.x, face.v3.screen.y );
		}
		
		private function createMidUV ( a:UV, b:UV ) : UV
		{
		    return new UV ( ( a.u + b.u ) * .5, ( a.v + b.v ) * .5 );
		}
		
	}
	
}
